struct ssvs_settings_t{
public:
  int good_model_threshold;
  int iterations;
  int samplesize;
  int variables;
  int models;
  int subsetsize;
  bool use_gpu;
  uint platform_id;
  uint device_id;
  string kernel_path;
  bool benchmark;
};

struct opencl_ssvs_t{

};

struct prefixsum_element{
  float sum;
  int index;
};

struct bySum{
  bool operator()(const prefixsum_element & p1,const prefixsum_element & p2) const{
    return(p1.sum<p2.sum);
  }
};

struct model_t{
  model_t(int subsetsize,int * variables,float likelihood);
  ~model_t();
  float likelihood;
  int subsetsize;
  vector<int> variables;
};

class SSVS:public Analyzer{
public:
  void init(const ptree & pt);
  SSVS();
  ~SSVS();
  void run();
private:
  MathUtils * math;
  ssvs_settings_t * settings;
  // GPU stuff
  // commandQueue is used to carry out memory transfers and kernel launches
  // need to be initialized before any thing else
  cl::CommandQueue * commandQueue;
  cl::Context * context;
  vector<cl::Device> devices;
  // handles to the various kernels go here
  cl::Program * program;
  cl::Kernel * kernel_simple;
  cl::Kernel * kernel_xprimex;
  cl::Kernel * kernel_invert;
  //cl::Kernel * kernel_compute_xxx;
  cl::Kernel * kernel_compute_beta;
  cl::Kernel * kernel_compute_rsq;
  
  cl::Buffer * buffer_subset;
  cl::Buffer * buffer_simple_in;
  cl::Buffer * buffer_simple_out;
  cl::Buffer * buffer_designmat;
  cl::Buffer * buffer_xx;
  cl::Buffer * buffer_inv_xx;
  //cl::Buffer * buffer_xxx;
  cl::Buffer * buffer_trait;
  cl::Buffer * buffer_beta;
  cl::Buffer * buffer_rsq;

  cl_int err;

  // DEBUGGING
  ofstream ofs;
  bool run_gpu;
  bool run_cpu;
  bool benchmark;
  cl::Event * event;
  // MPI stuff
  int mpi_numtasks;
  int mpi_rank;
  int rc;
  MPI_Status stat;
  MPI_Datatype VectorFloatArrayType;
  MPI_Datatype VectorIntArrayType;
  MPI_Datatype MatrixIntArrayType;
  static const int TAG_SYNC_POSTERIOR = 1;
  static const int TAG_GOOD_MODELS = 2;
  static const int TAG_GOOD_MODEL = 3;
  static const int TAG_GOOD_MODEL_LIKELIHOOD = 4;
  static const int TAG_ACCEPT_CODE = 6;
  static const int TAG_SYNC_MAX_LIKELIHOOD1 = 7;
  static const int TAG_SYNC_MAX_LIKELIHOOD2 = 8;

  void sample_variables();
  void fit_models();

  float current_best_likelihood;
  float alltime_best_likelihood;
  int samplesize;
  int variables;
  int subsetsize;
  int subsetsize2;
  int models;
  float * rsq_arr;
  int * subset;
  int * good_model;
  list<model_t> candidate_models;
  float * good_likelihoods;
  int good_models;
  float grandsum;
  int iterations;
  float rsq_threshold;
  int good_model_threshold;
  float trait_mean;
  float * previous_weight_vector;
  float * weight_vector;
  typedef set<prefixsum_element,bySum> prefixsum_set_type;
  prefixsum_set_type prefixsum_set;
  float tss;
  float * designmat;
  float * trait; //samplesize
  float * beta; // models*subsetsize
  float * xx; // models*subsetsize*subsetsize
  float * inv_xx; // models*subsetsize * subsetsize
  //float * xxx; // subsetsize * samplesize
  
  #ifdef USE_GPU
  #endif
  
  inline float ran(){
    return rand()/(RAND_MAX+1.);
  }
  void init_gpu(int * platform_id, int * device_id, const char * kernel_path);
  void init_buffers();
  void run_simple(int * scaler, int * return_vec);
  void invert_xprimex();
  //void invert();
  void compute_beta();
  void compute_rsq();
  void get_random_subset(int * subset);
  void update_prefixsum();
  void update_weight_vector();
  void sync_weight_vector();
  void get_top_likelihood();
  bool acceptreject_models();
  void sync_good_models();
  void list_posteriors();
};
